<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        @keyframes changeImgSrc {
            0% {
                opacity: 0.3;
            }

            100% {
                opacity: 1.0;
            }
        }

        .updateImgeSrc {
            animation: changeImgSrc 3s linear 0s 1 normal forwards;
        }
    </style>

    <style>
        @keyframes changeImgSrcBackwardCompatible {
            0% {
                width: 0%;
                opacity: 0.3;
                transform: rotate(0deg);
            }

            100% {
                width: 100%;
                opacity: 1.0;
                transform: rotate(1080deg);
            }
        }

        .updateImgeSrcBackwardCompatible {
            animation: changeImgSrcBackwardCompatible 3s linear 0s 1 normal forwards;
        }
    </style>

</head>

<body>

    <p>普通的图片替换</p>
    <div>
        <img src="img11.jpg" alt="" style="width: 300px;" onmouseover="changeImageSrc(this, 'img12.jpg');"
            onmouseout="changeImageSrc(this, 'img11.jpg');">
    </div>
    <script>
        function changeImageSrc(img, imgSrc) {
            img.src = imgSrc;
        }
    </script>

    <hr>

    <p>带有动画效果的图片替换（供有余力的读者参考）</p>
    <div style="width: 300px;">
        <img src="img11.jpg" alt="" style="width: 100%;" onmouseover="changeImageSrcWithAnimation(this, 'img12.jpg');"
            onmouseout="changeImageSrcWithAnimation(this, 'img11.jpg');">
    </div>
    <script>
        var i = 0;
        function changeImageSrcWithAnimation(img, imgSrc) {
            updateClassOfHtmlTag(img, 'updateImgeSrc', 'remove');

            // 直接执行，只有第一次才有动画效果。
            setTimeout(() => {
                img.src = imgSrc;
                updateClassOfHtmlTag(img, 'updateImgeSrc');
                console.log(img.getAttribute('class'));
            }, 100);
        }
    </script>

    <hr style="border: blue 2px solid;">

    <p>普通的图片替换（向后兼容）</p>
    <div style="width: 300px;">
        <a href="#" style="display:inline-block; width: 100%; height: 100%;"
            onmouseover="changeImageSrcBackwardCompatible(document.getElementById('img_1'), 'img12.jpg');"
            onmouseout="changeImageSrcBackwardCompatible(document.getElementById('img_1'), 'img11.jpg');">
            <img id="img_1" src="img11.jpg" alt="" style="width: 100%;">
        </a>
    </div>
    <script>
        function changeImageSrcBackwardCompatible(img, imgSrc) {
            img.src = imgSrc;
        }
    </script>

    <hr>

    <p>带有动画效果的图片替换（向后兼容）（供有余力的读者参考）</p>
    <div style="width: 320px; height: 200px;">
        <a id="container" href="#"
            style="display:inline-block; width: 100%; height: 100%; overflow: hidden; background-color: rgb(51, 51, 53);"
            onmouseenter="console.log(event);changeImageSrcWithAnimationBackwardCompatible(document.getElementById('img_2'), 'img12.jpg');"
            onmouseleave="console.log(event);changeImageSrcWithAnimationBackwardCompatible(document.getElementById('img_2'), 'img11.jpg');">
            <img id="img_2" src="img11.jpg" alt="" style="width: 100%;">
        </a>
    </div>
    <p>注意这里的样式：1）外围的div需要给定width和height；2）a标签需要指定width和height为100%；3）指定a标签的display属性为inline-block；4）a标签的overflow设置为hidden；
    </p>
    <p>使用mouseenter和mouseleave事件代替mouseover和mouseout，避免冒泡。</p>
    <script>
        function changeImageSrcWithAnimationBackwardCompatible(img, imgSrc) {
            updateClassOfHtmlTag(img, 'updateImgeSrcBackwardCompatible', 'remove');

            // 直接执行，只有第一次才有动画效果。
            setTimeout(() => {
                img.style.width = "0%";
                img.src = imgSrc;
                updateClassOfHtmlTag(img, 'updateImgeSrcBackwardCompatible');
                console.log(img.getAttribute('class'));
            }, 100);
        }
        // var container = document.getElementById('container');
        // var img = document.getElementById('img_2');
        // container.addEventListener('mouseenter', function name(event) {
        //     changeImageSrcWithAnimationBackwardCompatible(img, 'img12.jpg');
        // }, false);
        // container.addEventListener('mouseleave', function name(event) {
        //     changeImageSrcWithAnimationBackwardCompatible(img, 'img11.jpg');
        // });
    </script>

</body>

</html>


<script>
    // 这里定义一个字符串处理函数，用于处理html标签中样式类的类名称。

    /**
     * 更新html元素的class属性：添加或者删除一个class名称
     * 
     * @param {string|HtmlElement} tag html标签节点或者标签的id
     * @param {string} className css类名称 如果该字符串不存在，或者为空，则不执行任何操作。
     * @param {number|string} position 当前css类名称要被添加到的位置。
     *          范围为：[0, 当前样式类数组的长度]，低于或者大于边界值，则使用最近的边界值。
     *          也可以使用“prepend”和“append”来表示开始和结束边界。其它，默认为末尾边界。
     *          如果该值为“remove”，则删除该属性。
     */
    function updateClassOfHtmlTag(tag, className, position) {
        console.log('arguments: tag = ' +
            typeof tag == 'string' ? ('id=' + tag) : typeof tag == 'object' ? ('nodeName=' + tag.nodeName + ', className=' + tag.className) : tag
                + ', className = ' + className + ', position = ' + position);
        if (!tag) {
            return;
        }

        // 获取html元素
        var node = null;
        if (typeof tag == 'string') {
            // 如果它是该标签的id
            node = document.getElementById(tag);
        } else if (typeof HTMLElement === 'object' ? tag instanceof HTMLElement : tag && typeof tag === 'object' && tag.nodeType === 1 && typeof tag.nodeName === 'string') {
            // 如果它是HtmlElement。
            // 由于在Chrome,Opera中HTMLElement的类型为function,因此需要做浏览器兼容性判断 https://www.cnblogs.com/charlesxiao/p/4249746.html
            node = tag;
        } else {
            console.warn('given tag is not a html tag name or a HtmlElement');
            return;
        }

        //
        if (!className || typeof className != 'string') {
            return;
        } else if (className.trim() == '') {
            return;
        }

        // 获取该节点的class属性，将该属性值分割为数组，方便处理。
        var classNamesArr = null;
        var classNamesStr = node.getAttribute('class');
        console.log('before update class. class = ' + classNamesStr);
        // 如果原先没有class标签，或者为空，则直接赋值。
        if (!classNamesStr || classNamesStr.trim() == '') {
            if (position == 'remove') {
            } else {
                classNamesArr = [className];
            }
        } else {
            // 根据position的值，进行赋值。先评估真正的position值。 
            if (position == 'remove') {
                // 移除
                classNamesArr = classNamesStr.split('\s');
                classNamesArr.splice(classNamesArr.indexOf(className), 1);
            } else {
                // 添加
                classNamesArr = classNamesStr.split('\s');
                if (!position) {
                    position = classNamesArr.length;
                } else if (position == 'prepend') {
                    position = 0;
                } else if (position == 'append') {
                    position = classNamesArr.length;
                } else if (position < 0) {
                    position = 0;
                } else if (position > classNamesArr.length) {
                    position = classNamesArr.length;
                }
                classNamesArr.splice(position, 0, className);
            }
        }

        // 重写class属性
        if (!classNamesArr || classNamesArr.length == 0) {
            node.removeAttribute('class');
            console.log('after remove specified class. class = ' + node.getAttribute('class'));
        } else {
            classNamesStr = classNamesArr.join(' ').trim();
            node.setAttribute('class', classNamesStr);
            console.log('after set specified class. class = ' + classNamesArr.join(' ').trim());
        }
    }
</script>